/*     */ package com.zimbra.cs.imap;
/*     */ 
/*     */ import com.sun.mail.util.BASE64DecoderStream;
/*     */ import com.sun.mail.util.QPDecoderStream;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.ByteUtil.SegmentInputStream;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.Pair;
/*     */ import com.zimbra.common.util.StartOutOfBoundsException;
/*     */ import com.zimbra.common.util.StringUtil;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.mailbox.MailItem;
/*     */ import com.zimbra.cs.mime.Mime;
/*     */ import java.io.ByteArrayInputStream;
/*     */ import java.io.ByteArrayOutputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.io.OutputStream;
/*     */ import java.io.PrintStream;
/*     */ import java.util.Enumeration;
/*     */ import java.util.List;
/*     */ import javax.mail.MessagingException;
/*     */ import javax.mail.internet.MimeBodyPart;
/*     */ import javax.mail.internet.MimeMessage;
/*     */ import javax.mail.internet.MimePart;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ class ImapPartSpecifier
/*     */ {
/*  56 */   private int octetStart = -1; private int octetEnd = -1;
/*     */   
/*     */   ImapPartSpecifier(String cmd, String part, String modifier)
/*     */   {
/*  60 */     this.command = cmd;
/*  61 */     this.part = part;
/*  62 */     this.modifier = modifier;
/*     */   }
/*     */   
/*     */   ImapPartSpecifier(String cmd, String part, String modifier, int start, int count) {
/*  66 */     this(cmd, part, modifier);
/*  67 */     setPartial(start, count);
/*     */   }
/*     */   
/*     */   void setPartial(int start, int count) {
/*  71 */     if ((start >= 0) && (count >= 0)) {
/*  72 */       this.octetStart = start;
/*  73 */       this.octetEnd = (start + count);
/*     */     }
/*     */   }
/*     */   
/*     */   boolean isEntireMessage() {
/*  78 */     return (this.part.equals("")) && (this.modifier.equals(""));
/*     */   }
/*     */   
/*     */   ImapPartSpecifier setHeaders(List<String> headers) {
/*  82 */     this.requestedHeaders = headers;
/*  83 */     return this;
/*     */   }
/*     */   
/*     */   private String[] getHeaders() {
/*  87 */     if ((this.requestedHeaders == null) || (this.requestedHeaders.isEmpty())) {
/*  88 */       return NO_HEADERS;
/*     */     }
/*     */     
/*  91 */     String[] headers = new String[this.requestedHeaders.size()];
/*  92 */     for (int i = 0; i < this.requestedHeaders.size(); i++) {
/*  93 */       headers[i] = ((String)this.requestedHeaders.get(i));
/*     */     }
/*  95 */     return headers;
/*     */   }
/*     */   
/*  98 */   private static final String[] NO_HEADERS = new String[0];
/*     */   static final int MAX_PINNED_BINARY_CONTENT = 100000;
/*     */   private final String command;
/*     */   
/* 102 */   public String toString() { StringBuilder response = new StringBuilder(this.command);
/* 103 */     if ((this.command.equals("BODY")) || (this.command.equals("BINARY")) || (this.command.equals("BINARY.SIZE"))) {
/* 104 */       response.append('[').append(getSectionSpec()).append(']');
/*     */       
/*     */ 
/* 107 */       if (this.octetStart != -1) {
/* 108 */         response.append('<').append(this.octetStart).append('>');
/*     */       }
/*     */     }
/* 111 */     return response.toString();
/*     */   }
/*     */   
/*     */   String getCommand() {
/* 115 */     return this.command;
/*     */   }
/*     */   
/*     */   String getSectionPart() {
/* 119 */     return this.part;
/*     */   }
/*     */   
/*     */   String getSectionSpec() {
/* 123 */     StringBuilder sb = new StringBuilder();
/* 124 */     sb.append(this.part).append((this.part.equals("")) || (this.modifier.equals("")) ? "" : ".").append(this.modifier);
/* 125 */     if (this.requestedHeaders != null) {
/* 126 */       boolean first = true;
/* 127 */       sb.append(" (");
/* 128 */       for (String header : this.requestedHeaders) {
/* 129 */         sb.append(first ? "" : " ").append(header.toUpperCase());
/* 130 */         first = false;
/*     */       }
/* 132 */       sb.append(')');
/*     */     }
/* 134 */     return sb.toString();
/*     */   }
/*     */   
/*     */   void write(PrintStream ps, OutputStream os, Object obj) throws IOException, ImapPartSpecifier.BinaryDecodingException, ServiceException {
/* 138 */     InputStream is = null;
/*     */     try {
/* 140 */       Pair<Long, InputStream> contents = getContent(obj);
/* 141 */       is = contents == null ? null : (InputStream)contents.getSecond();
/* 142 */       long length = contents == null ? -1L : ((Long)contents.getFirst()).longValue();
/*     */       
/* 144 */       ps.print(this);
/* 145 */       ps.write(32);
/*     */       
/* 147 */       if (is == null) {
/* 148 */         ps.print("NIL");
/* 149 */       } else if (this.command.equals("BINARY.SIZE")) {
/* 150 */         ps.print(length >= 0L ? length : NULCheck.getLength(is));
/*     */       } else {
/* 152 */         boolean binary = false;
/* 153 */         if (this.command.startsWith("BINARY")) {
/* 154 */           NULCheck nul = NULCheck.hasNULs(is, length);
/* 155 */           if (length < 0L) {
/* 156 */             length = nul.length;
/*     */           }
/* 158 */           if (nul.content == null)
/*     */           {
/* 160 */             is = (InputStream)getContent(obj).getSecond();
/*     */           }
/*     */           else {
/* 163 */             is = new ByteArrayInputStream(nul.content);
/*     */           }
/* 165 */           binary = nul.hasNULs;
/*     */         }
/*     */         
/* 168 */         ps.print(binary ? "~{" : "{");
/* 169 */         ps.print(length);
/* 170 */         ps.write(125);
/* 171 */         if (os != null) {
/* 172 */           os.write(ImapHandler.LINE_SEPARATOR_BYTES);
/* 173 */           long written = ByteUtil.copy(is, false, os, false);
/* 174 */           if ((!$assertionsDisabled) && (written != length)) throw new AssertionError();
/*     */         }
/*     */       }
/*     */     } finally {
/* 178 */       ByteUtil.closeStream(is);
/*     */     }
/*     */   }
/*     */   
/*     */   Pair<Long, InputStream> getContent(Object obj) throws IOException, ImapPartSpecifier.BinaryDecodingException, ServiceException {
/*     */     Pair<Long, InputStream> contents;
/* 184 */     if ((obj instanceof MimeMessage)) {
/* 185 */       contents = getContent((MimeMessage)obj); } else { Pair<Long, InputStream> contents;
/* 186 */       if ((obj instanceof MailItem)) {
/* 187 */         if (!isEntireMessage()) {
/* 188 */           throw ServiceException.FAILURE("called writeMessage on non-toplevel part", null);
/*     */         }
/* 190 */         contents = ImapMessage.getContent((MailItem)obj);
/*     */       } else {
/* 192 */         throw ServiceException.FAILURE("called write() with unexpected argument: " + (obj == null ? "null" : obj.getClass().getSimpleName()), null);
/*     */       }
/*     */     }
/*     */     Pair<Long, InputStream> contents;
/* 196 */     if ((this.octetStart >= 0) && (contents != null))
/*     */     {
/*     */ 
/* 199 */       InputStream is = (InputStream)contents.getSecond();
/* 200 */       long statedLength = ((Long)contents.getFirst()).longValue();
/* 201 */       long realLength = Math.max(0L, Math.min(statedLength < 0L ? 2147483647L : statedLength, this.octetEnd) - this.octetStart);
/*     */       try {
/* 203 */         int start = this.octetStart;
/*     */         
/*     */ 
/* 206 */         if (((is instanceof BASE64DecoderStream)) || ((is instanceof QPDecoderStream))) {
/* 207 */           ByteUtil.skip(is, start);
/* 208 */           start = 0;
/*     */         }
/* 210 */         is = ByteUtil.SegmentInputStream.create(is, start, start + realLength);
/*     */       }
/*     */       catch (StartOutOfBoundsException e) {
/* 213 */         ZimbraLog.imap.warn("IMAP part requested start out of range", e);
/* 214 */         is = new ByteArrayInputStream(new byte[0]);
/* 215 */         statedLength = realLength = 0L;
/*     */       } catch (IOException ioe) {
/* 217 */         ByteUtil.closeStream(is);
/* 218 */         throw ioe;
/*     */       }
/* 220 */       contents = new Pair(Long.valueOf(statedLength < 0L ? -1L : realLength), is);
/*     */     }
/* 222 */     return contents;
/*     */   }
/*     */   
/*     */   private static class NULCheck {
/*     */     long length;
/*     */     byte[] content;
/*     */     boolean hasNULs;
/*     */     
/*     */     static long getLength(InputStream is) throws ImapPartSpecifier.BinaryDecodingException {
/* 231 */       return scan(is, -1L, false).length;
/*     */     }
/*     */     
/*     */     static NULCheck hasNULs(InputStream is, long length) throws ImapPartSpecifier.BinaryDecodingException {
/* 235 */       return scan(is, length, true);
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */     private static NULCheck scan(InputStream is, long length, boolean checkNULs)
/*     */       throws ImapPartSpecifier.BinaryDecodingException
/*     */     {
/* 243 */       NULCheck nul = new NULCheck();
/* 244 */       long totalSize = 0L;
/* 245 */       ByteArrayOutputStream baos = null;
/* 246 */       if ((checkNULs) && (length <= 100000L)) {
/* 247 */         baos = new ByteArrayOutputStream(length < 0L ? 50000 : (int)length);
/*     */       }
/*     */       
/* 250 */       byte[] buffer = new byte[' '];
/*     */       try {
/*     */         int bytesRead;
/* 253 */         while ((bytesRead = is.read(buffer)) >= 0)
/*     */         {
/* 255 */           if ((checkNULs) && (!nul.hasNULs)) {
/* 256 */             for (int i = 0; i < bytesRead; i++) {
/* 257 */               if (buffer[i] == 0) {
/* 258 */                 nul.hasNULs = true;
/* 259 */                 break;
/*     */               }
/*     */             }
/*     */           }
/*     */           
/* 264 */           if (length < 0L) {
/* 265 */             totalSize += bytesRead;
/* 266 */             if ((totalSize > 100000L) && (baos != null)) {
/* 267 */               baos = null;
/*     */             }
/*     */           }
/* 270 */           if (baos != null) {
/* 271 */             baos.write(buffer, 0, bytesRead);
/* 272 */           } else if ((nul.hasNULs) && (length > 0L)) {
/*     */             break;
/*     */           }
/*     */         }
/*     */       } catch (IOException ioe) {
/* 277 */         throw new ImapPartSpecifier.BinaryDecodingException();
/*     */       } finally {
/* 279 */         ByteUtil.closeStream(is);
/*     */       }
/*     */       
/* 282 */       if (baos != null) {
/* 283 */         nul.content = baos.toByteArray();
/*     */       }
/* 285 */       if (length < 0L) {
/* 286 */         nul.length = totalSize;
/*     */       }
/* 288 */       return nul;
/*     */     }
/*     */   }
/*     */   
/*     */   private boolean isMessageBody(MimeMessage base, MimePart resolved) throws IOException, MessagingException
/*     */   {
/* 294 */     if ((!this.part.equals("1")) && (!this.part.endsWith(".1"))) {
/* 295 */       return false;
/*     */     }
/*     */     
/* 298 */     String parentPart = this.part.substring(0, Math.max(0, this.part.length() - 2));
/* 299 */     return Mime.getMimePart(base, parentPart) == resolved;
/*     */   }
/*     */   
/*     */   private Pair<Long, InputStream> getContent(MimeMessage msg) throws ImapPartSpecifier.BinaryDecodingException {
/* 303 */     long length = -1L;
/* 304 */     InputStream is = null;
/*     */     try
/*     */     {
/* 307 */       MimePart mp = Mime.getMimePart(msg, this.part);
/* 308 */       if (mp == null) {
/* 309 */         return null;
/*     */       }
/*     */       
/*     */ 
/* 313 */       if (((this.modifier.equals("TEXT")) || (this.modifier.startsWith("HEADER"))) && (!(mp instanceof MimeMessage)))
/*     */       {
/* 315 */         Object content = Mime.getMessageContent(mp);
/* 316 */         if (!(content instanceof MimeMessage)) {
/* 317 */           return null;
/*     */         }
/* 319 */         mp = (MimeMessage)content;
/*     */       }
/*     */       
/*     */ 
/* 323 */       if (this.modifier.equals("")) {
/* 324 */         if ((mp instanceof MimeBodyPart)) {
/* 325 */           if (this.command.startsWith("BINARY")) {
/*     */             try {
/* 327 */               is = ((MimeBodyPart)mp).getInputStream();
/*     */             } catch (IOException ioe) {
/* 329 */               throw new BinaryDecodingException();
/*     */             }
/*     */           } else {
/* 332 */             is = ((MimeBodyPart)mp).getRawInputStream();
/* 333 */             length = Math.max(0, mp.getSize());
/*     */           }
/* 335 */         } else if ((mp instanceof MimeMessage)) {
/* 336 */           if (!isMessageBody(msg, mp)) {
/* 337 */             String parentPart = this.part.substring(0, Math.max(0, this.part.length() - 2));
/* 338 */             return new ImapPartSpecifier(this.command, parentPart, "TEXT").getContent(msg); }
/* 339 */           if (this.command.startsWith("BINARY")) {
/*     */             try {
/* 341 */               is = ((MimeMessage)mp).getInputStream();
/*     */             } catch (IOException ioe) {
/* 343 */               throw new BinaryDecodingException();
/*     */             }
/*     */           } else {
/* 346 */             is = ((MimeMessage)mp).getRawInputStream();
/* 347 */             length = Math.max(0, mp.getSize());
/*     */           }
/*     */         } else {
/* 350 */           ZimbraLog.imap.debug("getting content of part; not MimeBodyPart: " + this);
/* 351 */           return ImapMessage.EMPTY_CONTENT;
/*     */         }
/* 353 */       } else if (this.modifier.startsWith("HEADER")) {
/* 354 */         MimeMessage mm = (MimeMessage)mp;
/*     */         Enumeration<?> headers;
/* 356 */         Enumeration<?> headers; if (this.modifier.equals("HEADER")) {
/* 357 */           headers = mm.getAllHeaderLines(); } else { Enumeration<?> headers;
/* 358 */           if (this.modifier.equals("HEADER.FIELDS")) {
/* 359 */             headers = mm.getMatchingHeaderLines(getHeaders());
/*     */           } else
/* 361 */             headers = mm.getNonMatchingHeaderLines(getHeaders());
/*     */         }
/* 363 */         StringBuilder result = new StringBuilder();
/* 364 */         while (headers.hasMoreElements()) {
/* 365 */           result.append(headers.nextElement()).append(ImapHandler.LINE_SEPARATOR);
/*     */         }
/*     */         
/* 368 */         byte[] content = ImapHandler.LINE_SEPARATOR.getBytes();
/* 369 */         is = new ByteArrayInputStream(content);
/* 370 */         length = content.length;
/* 371 */       } else if (this.modifier.equals("MIME")) {
/* 372 */         if ((mp instanceof MimeMessage)) {
/* 373 */           String parentPart = this.part.substring(0, Math.max(0, this.part.length() - 2));
/* 374 */           return new ImapPartSpecifier(this.command, parentPart, "HEADER").getContent(msg);
/*     */         }
/*     */         
/* 377 */         Enumeration<?> mime = mp.getAllHeaderLines();
/* 378 */         StringBuilder result = new StringBuilder();
/* 379 */         while (mime.hasMoreElements()) {
/* 380 */           result.append(mime.nextElement()).append(ImapHandler.LINE_SEPARATOR);
/*     */         }
/*     */         
/* 383 */         byte[] content = ImapHandler.LINE_SEPARATOR.getBytes();
/* 384 */         is = new ByteArrayInputStream(content);
/* 385 */         length = content.length;
/* 386 */       } else if (this.modifier.equals("TEXT")) {
/* 387 */         is = ((MimeMessage)mp).getRawInputStream();
/* 388 */         length = Math.max(0, mp.getSize());
/*     */       } else {
/* 390 */         return null;
/*     */       }
/*     */       
/* 393 */       return new Pair(Long.valueOf(length), is);
/*     */     } catch (IOException e) {
/* 395 */       ByteUtil.closeStream(is);
/* 396 */       return null;
/*     */     } catch (MessagingException e) {
/* 398 */       ByteUtil.closeStream(is); }
/* 399 */     return null;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/* 404 */   public boolean isIgnoredExchangeHeader() { return (this.requestedHeaders != null) && (this.requestedHeaders.size() == 1) && (StringUtil.equalIgnoreCase((String)this.requestedHeaders.get(0), "CONTENT-CLASS")) && (StringUtil.equalIgnoreCase("BODY", this.command)) && (StringUtil.equalIgnoreCase("HEADER.FIELDS", this.modifier)); }
/*     */   
/*     */   private final String part;
/*     */   private final String modifier;
/*     */   private List<String> requestedHeaders;
/*     */   public int hashCode() {
/* 410 */     int prime = 31;
/* 411 */     int result = 1;
/* 412 */     result = 31 * result + (this.command == null ? 0 : this.command.hashCode());
/* 413 */     result = 31 * result + (this.requestedHeaders == null ? 0 : this.requestedHeaders.hashCode());
/* 414 */     result = 31 * result + (this.modifier == null ? 0 : this.modifier.hashCode());
/* 415 */     result = 31 * result + this.octetEnd;
/* 416 */     result = 31 * result + this.octetStart;
/* 417 */     result = 31 * result + (this.part == null ? 0 : this.part.hashCode());
/* 418 */     return result;
/*     */   }
/*     */   
/*     */   public boolean equals(Object obj)
/*     */   {
/* 423 */     if (this == obj) {
/* 424 */       return true;
/*     */     }
/* 426 */     if (obj == null) {
/* 427 */       return false;
/*     */     }
/* 429 */     if (getClass() != obj.getClass()) {
/* 430 */       return false;
/*     */     }
/* 432 */     ImapPartSpecifier other = (ImapPartSpecifier)obj;
/* 433 */     if (this.command == null) {
/* 434 */       if (other.command != null) {
/* 435 */         return false;
/*     */       }
/* 437 */     } else if (!this.command.equals(other.command)) {
/* 438 */       return false;
/*     */     }
/* 440 */     if (this.requestedHeaders == null) {
/* 441 */       if (other.requestedHeaders != null) {
/* 442 */         return false;
/*     */       }
/* 444 */     } else if (!this.requestedHeaders.equals(other.requestedHeaders)) {
/* 445 */       if ((other.requestedHeaders == null) || (this.requestedHeaders.size() != other.requestedHeaders.size())) {
/* 446 */         return false;
/*     */       }
/*     */       
/*     */ 
/* 450 */       return this.requestedHeaders.containsAll(other.requestedHeaders);
/*     */     }
/*     */     
/* 453 */     if (this.modifier == null) {
/* 454 */       if (other.modifier != null) {
/* 455 */         return false;
/*     */       }
/* 457 */     } else if (!this.modifier.equals(other.modifier)) {
/* 458 */       return false;
/*     */     }
/* 460 */     if (this.octetEnd != other.octetEnd) {
/* 461 */       return false;
/*     */     }
/* 463 */     if (this.octetStart != other.octetStart) {
/* 464 */       return false;
/*     */     }
/* 466 */     if (this.part == null) {
/* 467 */       if (other.part != null) {
/* 468 */         return false;
/*     */       }
/* 470 */     } else if (!this.part.equals(other.part)) {
/* 471 */       return false;
/*     */     }
/* 473 */     return true;
/*     */   }
/*     */   
/*     */   static class BinaryDecodingException
/*     */     extends Exception
/*     */   {
/*     */     private static final long serialVersionUID = 8158363540973909369L;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/imap/ImapPartSpecifier.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */